package com.cicc.beergamevirtualsimulationsystem.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cicc.beergamevirtualsimulationsystem.common.enums.commonEnum.LevelEnum;
import com.cicc.beergamevirtualsimulationsystem.common.enums.roomEnum.Status;
import com.cicc.beergamevirtualsimulationsystem.common.enums.userTypeEnum.RoleType;
import com.cicc.beergamevirtualsimulationsystem.common.errorEnum.ParameterErrorEnum;
import com.cicc.beergamevirtualsimulationsystem.common.errorEnum.RoleBindingErrorEnum;
import com.cicc.beergamevirtualsimulationsystem.common.errorEnum.RoomErrorEnum;
import com.cicc.beergamevirtualsimulationsystem.common.exception.BizException;
import com.cicc.beergamevirtualsimulationsystem.common.exception.ExceptionFactory;
import com.cicc.beergamevirtualsimulationsystem.context.BaseContext;
import com.cicc.beergamevirtualsimulationsystem.entity.ParameterEntity;
import com.cicc.beergamevirtualsimulationsystem.entity.RoleBindingEntity;
import com.cicc.beergamevirtualsimulationsystem.entity.RoleTypeEntity;
import com.cicc.beergamevirtualsimulationsystem.entity.RoomEntity;
import com.cicc.beergamevirtualsimulationsystem.mapper.RoomMapper;
import com.cicc.beergamevirtualsimulationsystem.model.parameter.dto.RoomParameterDto;
import com.cicc.beergamevirtualsimulationsystem.model.roleBinding.RoleBindingReq;
import com.cicc.beergamevirtualsimulationsystem.model.room.dto.CreateRoomDto;
import com.cicc.beergamevirtualsimulationsystem.model.room.dto.QueryJoinRoomDto;
import com.cicc.beergamevirtualsimulationsystem.model.room.dto.QueryRoomDto;
import com.cicc.beergamevirtualsimulationsystem.model.room.dto.QueryRoomRecordDto;
import com.cicc.beergamevirtualsimulationsystem.model.room.req.CreateRoomReq;
import com.cicc.beergamevirtualsimulationsystem.model.userType.RoleTypeReq;
import com.cicc.beergamevirtualsimulationsystem.service.ParameterService;
import com.cicc.beergamevirtualsimulationsystem.service.RoleBindingService;
import com.cicc.beergamevirtualsimulationsystem.service.RoleTypeService;
import com.cicc.beergamevirtualsimulationsystem.service.RoomService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;


@Service
@RequiredArgsConstructor
public class RoomServiceImpl extends ServiceImpl<RoomMapper, RoomEntity> implements RoomService {

    private final RoomMapper roomMapper;

    private final ParameterService parameterService;

    private final RoleTypeService roleTypeService;

    private final RoleBindingService roleBindingService;


    /**
     * 根据房间口令查询房间号
     *
     * @param command
     * @return
     */
    @Override
    public QueryRoomDto queryRoomByCommand(String command) {
        LambdaQueryWrapper<RoomEntity> wrapper = new LambdaQueryWrapper<RoomEntity>()
                .eq(RoomEntity::getCommand, command)
                .eq(RoomEntity::getStatus, Status.NOT_START.getCode());
        RoomEntity roomEntity = roomMapper.selectOne(wrapper);
        if (Objects.isNull(roomEntity)) {
            throw new BizException(RoomErrorEnum.ROOM_NOT_EXIST_ERROR.getCode(), RoomErrorEnum.ROOM_NOT_EXIST_ERROR.getDesc());
        }
        QueryRoomDto roomDto = new QueryRoomDto();
        roomDto.setRoomId(roomEntity.getId());
        roomDto.setCreatorId(roomEntity.getCreatorId());
        roomDto.setCommand(command);
        return roomDto;
    }

    /**
     * 创建房间
     *
     * @param createRoomReq
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CreateRoomDto createRoom(CreateRoomReq createRoomReq) {
        // 获取房间口令
        String command = getRoomCommand();
        // 校验是否存在重复创建房间
        checkRepeatRoom();
        // 不能创建房间  --> 当前用户已经参与了一把游戏
        QueryJoinRoomDto queryJoinRoomDto = queryGame();
        if (!Objects.isNull(queryJoinRoomDto.getRoomId())) {
            throw ExceptionFactory.bizException(RoomErrorEnum.USER_HAS_JOIN_GAME_ERROR.getCode(), RoomErrorEnum.USER_HAS_JOIN_GAME_ERROR.getDesc());
        }
        // 创建房间
        RoomEntity room = new RoomEntity();
        room.setCreatorId(BaseContext.getCurrentId());
        room.setCreateTime(String.valueOf(System.currentTimeMillis()));
        room.setCommand(command);
        room.setStatus(Status.NOT_START.getCode());
        roomMapper.insert(room);

        // 保存参数设置
        ParameterEntity parameterEntity = new ParameterEntity();
        BeanUtils.copyProperties(createRoomReq, parameterEntity);
        String id = room.getId();
        parameterEntity.setRoomId(id);
        parameterEntity.setLevel(LevelEnum.USER_DEFINED.getCode());
        parameterService.save(parameterEntity);

        // 保存角色类型表
        List<RoleTypeReq> roleTypeReqList = createRoomReq.getRoleTypeList();

        // 校验人数
        validPeople(roleTypeReqList, createRoomReq.getTotalPeopleNum());

        List<RoleTypeEntity> roleTypeEntities = new ArrayList<>();
        roleTypeReqList.forEach(v -> {
            RoleTypeEntity roleTypeEntity = new RoleTypeEntity();
            BeanUtils.copyProperties(v, roleTypeEntity);
            roleTypeEntity.setRoomId(room.getId());
            roleTypeEntity.setLevel(LevelEnum.USER_DEFINED.getCode());
            roleTypeEntities.add(roleTypeEntity);
        });

        // 手动添加主持人角色配置信息
        RoleTypeEntity roleTypeEntity = RoleTypeEntity.masterInfo(room.getId());
        roleTypeEntities.add(roleTypeEntity);

        roleTypeService.saveBatch(roleTypeEntities);

        // 返回创建房间的信息
        CreateRoomDto createRoomDto = new CreateRoomDto();
        createRoomDto.setRoomId(room.getId());
        createRoomDto.setCreatorId(BaseContext.getCurrentId());
        createRoomDto.setCommand(command);
        return createRoomDto;
    }

    /**
     * 校验是否存在重复创建房间
     */
    private void checkRepeatRoom() {
        // 不能重复创建房间 --> 房主同时拥有多个未开始的房间和已开始的房间
        RoomEntity roomEntity = roomMapper.selectOne(new LambdaQueryWrapper<RoomEntity>()
                .eq(RoomEntity::getCreatorId, BaseContext.getCurrentId())
                .in(RoomEntity::getStatus, Status.NOT_START.getCode(), Status.STARTED.getCode()));
        if (!Objects.isNull(roomEntity)) {
            throw ExceptionFactory.bizException(RoomErrorEnum.ROOM_HAS_REPEAT_ERROR.getCode(), String.format(RoomErrorEnum.ROOM_HAS_REPEAT_ERROR.getDesc(), roomEntity.getCommand()));
        }
    }

    /**
     * 获取房间口令
     * 优先提供简易口令
     *
     * @return
     */
    private String getRoomCommand() {
        List<String> roomCommand = new ArrayList<>(Arrays.asList("1111", "2222", "3333", "4444", "5555", "6666", "7777", "8888", "9999", "0000"));
        LambdaQueryWrapper<RoomEntity> wrapper = new LambdaQueryWrapper<RoomEntity>()
                .in(RoomEntity::getCommand, roomCommand)
                // status不为结束 或者 当前时间没有超过结束时间
                .eq(RoomEntity::getStatus, Status.NOT_START.getCode());
        // 获取有已经使用过的口令
        List<String> roomCommandList = roomMapper.selectList(wrapper).stream().map(RoomEntity::getCommand).distinct().toList();
        // 排除已经使用过的口令
        roomCommand.removeAll(roomCommandList);
        if (!roomCommand.isEmpty()) {
            return roomCommand.get(0);
        }
        return RandomUtil.randomNumbers(4);
    }

    /**
     * 开始游戏
     *
     * @param roomId
     * @param roleBindingReqList
     */
    @Override
    public void startGame(String roomId, String masterId, List<RoleBindingReq> roleBindingReqList) {
        // 校验是否已经分配角色信息
        long flag = roleBindingService.count(new LambdaQueryWrapper<RoleBindingEntity>().eq(
                RoleBindingEntity::getRoomId, roomId
        ));
        if (flag > 0) {
            throw ExceptionFactory.bizException(RoleBindingErrorEnum.ROLE_HAS_BINDING_ERROR.getCode(), RoleBindingErrorEnum.ROLE_HAS_BINDING_ERROR.getDesc());
        }

        // 客户
        RoleBindingEntity customerRoleBinding = roleBindingReqList.stream().filter(v -> v.getRoleType().equals(RoleType.CUSTOMER.getCode())).findFirst().map(v -> {
            RoleBindingEntity customerRole = new RoleBindingEntity();
            BeanUtils.copyProperties(v, customerRole);
            customerRole.setRoleName(RoleType.CUSTOMER.getDesc());
            return customerRole;
        }).get();

        // 工厂
        List<RoleBindingEntity> factoryRoleBindingList = roleBindingReqList.stream().filter(v -> v.getRoleType().equals(RoleType.FACTORY.getCode())).map(v -> {
            RoleBindingEntity factoryRoleBinding = new RoleBindingEntity();
            BeanUtils.copyProperties(v, factoryRoleBinding);
            return factoryRoleBinding;
        }).toList();

        // 供应商
        List<RoleBindingEntity> supplierRoleBindingList = roleBindingReqList.stream().filter(v -> v.getRoleType().equals(RoleType.SUPPLIER.getCode())).map(v -> {
            RoleBindingEntity factoryRoleBinding = new RoleBindingEntity();
            BeanUtils.copyProperties(v, factoryRoleBinding);
            return factoryRoleBinding;
        }).toList();

        // 零售商
        List<RoleBindingEntity> retailerRoleBindingList = roleBindingReqList.stream().filter(v -> v.getRoleType().equals(RoleType.RETAILER.getCode())).map(v -> {
            RoleBindingEntity factoryRoleBinding = new RoleBindingEntity();
            BeanUtils.copyProperties(v, factoryRoleBinding);
            return factoryRoleBinding;
        }).toList();

        // 分配工厂和供应商人数
        allocatePersonnel(factoryRoleBindingList, supplierRoleBindingList, masterId);

        // 分配供应商和零售商人数
        allocatePersonnel(supplierRoleBindingList, retailerRoleBindingList, masterId);

        // 分配零售商的下游为 主持人id
        allocatePersonnel(retailerRoleBindingList, new ArrayList<>(), masterId);

        // 将三个list全部合并 存入数据库
        List<RoleBindingEntity> roleBindingEntities = new ArrayList<>();
        roleBindingEntities.addAll(factoryRoleBindingList);
        roleBindingEntities.addAll(supplierRoleBindingList);
        roleBindingEntities.addAll(retailerRoleBindingList);
        roleBindingEntities.add(customerRoleBinding);
        roleBindingService.saveBatch(roleBindingEntities);

        // 设置当前房间状态为开始状态
        RoomEntity room = new RoomEntity();
        room.setId(roomId);
        room.setStatus(Status.STARTED.getCode());
        roomMapper.updateById(room);
    }

    /**
     * 解散房间  修改房间状态为关闭
     *
     * @param roomId
     * @return
     */
    @Override
    public Boolean closeRoom(String roomId) {
        return update(new LambdaUpdateWrapper<RoomEntity>().set(RoomEntity::getStatus, Status.ENDED.getCode()).eq(RoomEntity::getId, roomId));
    }

    /**
     * 关闭房间 多个
     *
     * @param roomId
     * @return
     */
    @Override
    public Boolean closeRoom(List<String> roomId) {
        return update(new LambdaUpdateWrapper<RoomEntity>().set(RoomEntity::getStatus, Status.ENDED.getCode()).in(RoomEntity::getId, roomId));
    }

    /**
     * 查询是否存在已参与的游戏
     *
     * @return 房间id
     */
    @Override
    public QueryJoinRoomDto queryGame() {
        QueryJoinRoomDto queryJoinRoomDto = new QueryJoinRoomDto();
        String userId = BaseContext.getCurrentId();
        LambdaQueryWrapper<RoleBindingEntity> wrapper = new LambdaQueryWrapper<RoleBindingEntity>().eq(RoleBindingEntity::getUserId, userId);
        List<String> roomIds = roleBindingService.list(wrapper).stream().map(RoleBindingEntity::getRoomId).toList();
        if(roomIds.isEmpty()){
            return queryJoinRoomDto;
        }
        // TODO 判断是否唯一
        RoomEntity room = roomMapper.selectOne(new LambdaQueryWrapper<RoomEntity>()
                .eq(RoomEntity::getStatus, Status.STARTED.getCode())
                .in(RoomEntity::getId, roomIds));
        if (!Objects.isNull(room)) {
            queryJoinRoomDto.setRoomId(room.getId());
            queryJoinRoomDto.setCreatorId(room.getCreatorId());
        }
        return queryJoinRoomDto;
    }

    /**
     * 用户游戏历史记录
     *
     * @return
     */
    @Override
    public List<QueryRoomRecordDto> queryRoomRecord() {
        String userId = BaseContext.getCurrentId();
        return roleBindingService.queryRoomRecord(userId);
    }

    /**
     * 查看房间状态
     */
    @Override
    public Integer queryRoomStatus(String roomId) {
        LambdaQueryWrapper<RoomEntity> wrapper = new LambdaQueryWrapper<RoomEntity>().eq(RoomEntity::getId, roomId);
        RoomEntity roomEntity = roomMapper.selectOne(wrapper);
        return roomEntity.getStatus();
    }

    /**
     * 再来一把
     *
     * @param oldRoomId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CreateRoomDto playAgain(String oldRoomId) {
        ParameterEntity parameterEntity = parameterService.getOne(new LambdaQueryWrapper<ParameterEntity>().eq(ParameterEntity::getRoomId, oldRoomId));
        if (Objects.isNull(parameterEntity)) {
            throw ExceptionFactory.bizException(ParameterErrorEnum.PARAMETER_NULL_ERROR.getCode(), ParameterErrorEnum.PARAMETER_NULL_ERROR.getDesc());
        }
        // 校验是否存在重复创建房间
        checkRepeatRoom();
        // 生成口令 并新建房间
        String command = getRoomCommand();
        RoomEntity room = new RoomEntity();
        room.setCommand(command);
        room.setCreatorId(BaseContext.getCurrentId());
        room.setStatus(Status.NOT_START.getCode());
        room.setCreateTime(String.valueOf(System.currentTimeMillis()));
        roomMapper.insert(room);
        // 新建房间参数
        String roomId = room.getId();
        parameterEntity.setRoomId(roomId);
        parameterEntity.setId(null);
        parameterService.save(parameterEntity);
        // 获取房间角色类型数据
        List<RoleTypeEntity> roleTypeEntities = roleTypeService.list(new LambdaQueryWrapper<RoleTypeEntity>().eq(RoleTypeEntity::getRoomId, oldRoomId))
                .stream().peek(v -> {
                    v.setId(null);
                    v.setRoomId(roomId);
                }).toList();
        roleTypeService.saveBatch(roleTypeEntities);
        CreateRoomDto createRoomDto = new CreateRoomDto();
        createRoomDto.setCommand(command);
        createRoomDto.setRoomId(roomId);
        createRoomDto.setCreatorId(BaseContext.getCurrentId());
        return createRoomDto;
    }


    /**
     * 分配人数
     *
     * @param upstreamList 上游
     * @param downstream   下游
     */
    private void allocatePersonnel(List<RoleBindingEntity> upstreamList, List<RoleBindingEntity> downstream, String masterId) {
        int upstreamSize = upstreamList.size();
        int downstreamSize = downstream.size();
        // key: 用户id  value:下游ids
        Map<String, List<String>> resultMap = new HashMap<>();
        // 大写A从65开始 ascii码
        int index = 65;
        // 初始化map
        for (RoleBindingEntity roleBindingEntity : upstreamList) {
            // 工厂 + A  --> 工厂A
            String roleName = RoleType.of(roleBindingEntity.getRoleType()) + (char) index;
            roleBindingEntity.setRoleName(roleName);
            resultMap.put(roleBindingEntity.getUserId(), new ArrayList<>());
            index++;
        }
        // 为下游指定上游 并存入map中
        for (int i = 0; i < downstreamSize; i++) {
            // 获取对应上下游
            RoleBindingEntity downStreamroleBindingEntity = downstream.get(i);
            RoleBindingEntity upStreamroleBindingEntity = upstreamList.get(i % upstreamSize);
            // 设置上游信息
            downStreamroleBindingEntity.setUpStreamId(upStreamroleBindingEntity.getUserId());
            // 存入map中
            resultMap.get(upStreamroleBindingEntity.getUserId()).add(downStreamroleBindingEntity.getUserId());
        }
        // 为上游指定下游
        upstreamList.forEach(v -> {
            String userId = v.getUserId();
            List<String> ids = resultMap.get(userId);
            if (ids.isEmpty()) {
                ids.add(masterId);
            }
            String downSteamListJson = JSONUtil.toJsonStr(ids);
            v.setDownStreamIds(downSteamListJson);
        });
    }

    /**
     * 校验人数
     *
     * @param roleTypeReqList
     */
    private void validPeople(List<RoleTypeReq> roleTypeReqList, Integer totalNum) {
        // 供应商人数
        int supplyNum = 0;
        // 零售商人数
        int retailNum = 0;
        // 工厂人数
        int factoryNum = 0;

        // 累加不同的人数
        for (RoleTypeReq roleTypeReq : roleTypeReqList) {
            if (roleTypeReq.getRoleType().equals(RoleType.FACTORY.getCode())) {
                factoryNum += roleTypeReq.getRoleNum();
            }
            if (roleTypeReq.getRoleType().equals(RoleType.SUPPLIER.getCode())) {
                supplyNum += roleTypeReq.getRoleNum();
            }
            if (roleTypeReq.getRoleType().equals(RoleType.RETAILER.getCode())) {
                retailNum += roleTypeReq.getRoleNum();
            }
        }
        // 总人数不匹配
        if (supplyNum + retailNum + factoryNum != totalNum) {
            throw new BizException(RoomErrorEnum.NUMBER_MISMATCH_ERROR.getCode(), RoomErrorEnum.NUMBER_MISMATCH_ERROR.getDesc());
        }
        // 供应商人数需要少于零售商人数
        if (supplyNum > retailNum) {
            throw new BizException(RoomErrorEnum.SUPPLIER_EXCEED_RETAILER_ERROR.getCode(), RoomErrorEnum.SUPPLIER_EXCEED_RETAILER_ERROR.getDesc());
        }
        // 工厂人数需要少于供应商人数
        if (factoryNum > supplyNum) {
            throw new BizException(RoomErrorEnum.FACTORY_EXCEED_SUPPLIER_ERROR.getCode(), RoomErrorEnum.FACTORY_EXCEED_SUPPLIER_ERROR.getDesc());
        }
    }
}
